#include "boundingboxeffect.h"
#include "vectorproperties.h"
#include "datatypedefine.h"
#include "transformableactor.h"
#include <osg\StateSet>
#include <osg\Node>
#include <osg\BlendFunc>
#include <osg\BlendColor>
#include <osg\Material>
#include <osg\Depth>
#include <osg\Geometry>
#include <osg\ComputeBoundsVisitor>

namespace VrDataCore
{
	BoundingBoxEffect::BoundingBoxEffect()
	{
		_materialValue = osg::Vec4(0.0f,0.0f,1.0f,0.6f);
		_constMaterial = _materialValue;
		_add = true;
	}

	BoundingBoxEffect::~BoundingBoxEffect()
	{}

	void BoundingBoxEffect::buildPropertyMap()
	{}

	void BoundingBoxEffect::setMaterial(osg::Vec4 offset)
	{
		_materialValue = offset;
		_constMaterial = _materialValue;
		if(_bindingMeshDrawableActor!=NULL && !isIdle())
		{
			unApply(_bindingMeshDrawableActor);
			apply(dynamic_cast<TransformableActor*>(_bindingMeshDrawableActor));
		}
	}

	osg::Vec4 BoundingBoxEffect::getMaterial()
	{
		return _materialValue;
	}

	void BoundingBoxEffect::apply( CoreBase::ActorBase* actor)
	{
		if( !isIdle() )
			return ;
		EffectBase::apply(actor);
		osg::Vec3 trans;
		osg::Vec3 rota;
		TransformableActor* transactor = dynamic_cast<TransformableActor*>(actor);
		if(transactor!=NULL)
		{
			trans = transactor->getTranslation();
			rota = transactor->getRotation();
			transactor->setTranslation(osg::Vec3(0,0,0));
			transactor->setRotation(osg::Vec3(0,0,0));
		}
		osg::Node* actornode = actor->getDrawable()->getOSGNode();
		osg::BoundingBox planebb;
		osg::ref_ptr<osg::ComputeBoundsVisitor> boundsVistor = new osg::ComputeBoundsVisitor();
		boundsVistor->reset() ;
		actornode->accept(*boundsVistor.get());
		planebb = boundsVistor->getBoundingBox();
		if(transactor!=NULL)
		{
			transactor->setTranslation(trans);
			transactor->setRotation(rota);
		}
		osg::ref_ptr<osg::Geode> boxnode = createBoundingbox(planebb,_materialValue,false);
		boxnode->addUpdateCallback(new BoundingBoxUpdateCallback(this));
		actor->getDrawable()->getOSGNode()->asGroup()->addChild(boxnode);
	}

	void BoundingBoxEffect::unApply(CoreBase::ActorBase* actor)
	{
		actor->getDrawable()->getOSGNode()->asGroup()->removeChild(_boundingBoxGeode);
		EffectBase::unApply(actor);
	}

	osg::ref_ptr<CoreBase::ActorBase> BoundingBoxEffect::clone()
	{
		osg::ref_ptr<CoreBase::ActorBase> clone = CoreBase::ActorBase::clone();
		return clone;
	}

	osg::ref_ptr<osg::Geode> BoundingBoxEffect::createBoundingbox(osg::BoundingBox& box,osg::Vec4& color,bool opacity)
	{
		_boundingBoxGeode = new osg::Geode ;
		osg::StateSet* ss = _boundingBoxGeode->getOrCreateStateSet() ;
		ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF|osg::StateAttribute::PROTECTED) ;
		osg::ref_ptr<osg::Program> program = new osg::Program;
		program->setName("boundingbox program");
		ss->setAttributeAndModes( program.get(), osg::StateAttribute::ON );
		if(!opacity)
		{
			osg::BlendFunc* bf = new
				osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,osg::BlendFunc::ONE_MINUS_SRC_ALPHA );
			ss->setAttributeAndModes(bf);
			ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
			ss->setRenderBinDetails(20, "DepthSortedBin"); 
		}
		float offset = box.radius()*0.05f;
		_boundingBoxGeometry = new osg::Geometry;
		osg::Vec3Array* vertex = new osg::Vec3Array;
		osg::Vec4Array* colorarray = new osg::Vec4Array ;
		colorarray->push_back( color ) ;
		osg::Vec3 vset[8] = {
			osg::Vec3( box._min._v[0]-offset , box._min._v[1]-offset, box._min._v[2]-offset),
			osg::Vec3( box._max._v[0]+ offset , box._min._v[1]-offset, box._min._v[2]-offset),
			osg::Vec3( box._max._v[0] + offset, box._min._v[1] - offset, box._max._v[2]+ offset),
			osg::Vec3( box._min._v[0] -offset , box._min._v[1]- offset, box._max._v[2]+ offset),

			osg::Vec3( box._min._v[0] -offset , box._max._v[1]+offset, box._min._v[2]-offset),
			osg::Vec3( box._max._v[0] +offset, box._max._v[1]+offset, box._min._v[2]-offset),
			osg::Vec3( box._max._v[0] +offset, box._max._v[1]+offset, box._max._v[2]+offset),
			osg::Vec3( box._min._v[0] -offset, box._max._v[1]+offset, box._max._v[2]+offset),
		} ;
		for( int i=0; i<4; i++)
			vertex->push_back(vset[i]);
		for( int i=3; i>=0; i-- )
			vertex->push_back(vset[i+4]);
		vertex->push_back(vset[0]) ;
		vertex->push_back(vset[4]) ;
		vertex->push_back(vset[5]) ;
		vertex->push_back(vset[1]) ;

		vertex->push_back(vset[3]) ;
		vertex->push_back(vset[2]) ;
		vertex->push_back(vset[6]) ;
		vertex->push_back(vset[7]) ;

		vertex->push_back(vset[0]) ;
		vertex->push_back(vset[3]) ;
		vertex->push_back(vset[7]) ;
		vertex->push_back(vset[4]) ;

		vertex->push_back(vset[1]) ;
		vertex->push_back(vset[5]) ;
		vertex->push_back(vset[6]) ;
		vertex->push_back(vset[2]) ;

		_boundingBoxGeometry->setVertexArray( vertex ) ;
		_boundingBoxGeometry->setColorArray( colorarray ) ;
		_boundingBoxGeometry->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);
		_boundingBoxGeometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, vertex->size())) ;
		_boundingBoxGeode->addDrawable(_boundingBoxGeometry) ;
		return _boundingBoxGeode ;
	}

	void BoundingBoxEffect::update()
	{
		if(_add)
		{
			if(_materialValue.r()<1.0f)
				_materialValue.r() = _materialValue.r() + 0.05f;
			if(_materialValue.g()<1.0f)
				_materialValue.g() = _materialValue.g() + 0.05f;
			if(_materialValue.b()<1.0f)
				_materialValue.b() = _materialValue.b() + 0.05f;
			if(_materialValue.r() >=1.0f && _materialValue.g() >=1.0f && _materialValue.b() >= 1.0f)
			{
				_materialValue.r() = 1.0f;
				_materialValue.g() = 1.0f;
				_materialValue.b() = 1.0f;
				_add = false;
			}
		}
		else
		{
			if(_materialValue.r()>_constMaterial.r())
				_materialValue.r() = _materialValue.r() - 0.05f;
			if(_materialValue.g()>_constMaterial.g())
				_materialValue.g() = _materialValue.g() - 0.05f;
			if(_materialValue.b()>_constMaterial.b())
				_materialValue.b() = _materialValue.b() - 0.05f;
			if(_materialValue.r() <= _constMaterial.r() && 
				_materialValue.g()<= _constMaterial.g() && _materialValue.b() <= _constMaterial.b())
			{
				_materialValue = _constMaterial;
				_add = true;
			}
		}
		osg::Vec4Array* colorarray = dynamic_cast<osg::Vec4Array*>(_boundingBoxGeometry->getColorArray());
		colorarray->clear();
		colorarray->push_back(_materialValue);
		_boundingBoxGeometry->setColorArray(colorarray);
	}
}